/*
 *   Copyright 2021 zzh
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 */

package com.aduib.boot.common.util;

import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @description: StrUtils
 * @author: zzh
 * @date: 2021/10/28 09:26
 */
public class StrUtils {
  public static final int INDEX_NOT_FOUND = -1;

  /**
   * 字符串常量：{@code "null"} <br>
   * 注意：{@code "null" != null}
   */
  public static final String NULL = "null";

  /** 字符串常量：空字符串 {@code ""} */
  public static final String EMPTY = "";

  /** 字符串常量：空格符 {@code " "} */
  public static final String SPACE = " ";

  /** 字符常量：空格符 {@code ' '} */
  public static final char C_SPACE = CharUtils.SPACE;

  /** 字符常量：制表符 {@code '\t'} */
  public static final char C_TAB = CharUtils.TAB;

  /** 字符常量：点 {@code '.'} */
  public static final char C_DOT = CharUtils.DOT;

  /** 字符常量：斜杠 {@code '/'} */
  public static final char C_SLASH = CharUtils.SLASH;

  /** 字符常量：反斜杠 {@code '\\'} */
  public static final char C_BACKSLASH = CharUtils.BACKSLASH;

  /** 字符常量：回车符 {@code '\r'} */
  public static final char C_CR = CharUtils.CR;

  /** 字符常量：换行符 {@code '\n'} */
  public static final char C_LF = CharUtils.LF;

  /** 字符常量：下划线 {@code '_'} */
  public static final char C_UNDERLINE = CharUtils.UNDERLINE;

  /** 字符常量：逗号 {@code ','} */
  public static final char C_COMMA = CharUtils.COMMA;

  /** 字符常量：花括号（左） <code>'{'</code> */
  public static final char C_DELIM_START = CharUtils.DELIM_START;

  /** 字符常量：花括号（右） <code>'}'</code> */
  public static final char C_DELIM_END = CharUtils.DELIM_END;

  /** 字符常量：中括号（左） {@code '['} */
  public static final char C_BRACKET_START = CharUtils.BRACKET_START;

  /** 字符常量：中括号（右） {@code ']'} */
  public static final char C_BRACKET_END = CharUtils.BRACKET_END;

  /** 字符常量：冒号 {@code ':'} */
  public static final char C_COLON = CharUtils.COLON;

  /** 字符常量：艾特 {@code '@'} */
  public static final char C_AT = CharUtils.AT;

  /** 字符串常量：制表符 {@code "\t"} */
  public static final String TAB = "	";

  /** 字符串常量：点 {@code "."} */
  public static final String DOT = ".";

  /**
   * 字符串常量：双点 {@code ".."} <br>
   * 用途：作为指向上级文件夹的路径，如：{@code "../path"}
   */
  public static final String DOUBLE_DOT = "..";

  /** 字符串常量：斜杠 {@code "/"} */
  public static final String SLASH = "/";

  /** 字符串常量：反斜杠 {@code "\\"} */
  public static final String BACKSLASH = "\\";

  /**
   * 字符串常量：回车符 {@code "\r"} <br>
   * 解释：该字符常用于表示 Linux 系统和 MacOS 系统下的文本换行
   */
  public static final String CR = "\r";

  /** 字符串常量：换行符 {@code "\n"} */
  public static final String LF = "\n";

  /**
   * 字符串常量：Windows 换行 {@code "\r\n"} <br>
   * 解释：该字符串常用于表示 Windows 系统下的文本换行
   */
  public static final String CRLF = "\r\n";

  /** 字符串常量：下划线 {@code "_"} */
  public static final String UNDERLINE = "_";

  /** 字符串常量：减号（连接符） {@code "-"} */
  public static final String DASHED = "-";

  /** 字符串常量：逗号 {@code ","} */
  public static final String COMMA = ",";

  /** 字符串常量：花括号（左） <code>"{"</code> */
  public static final String DELIM_START = "{";

  /** 字符串常量：花括号（右） <code>"}"</code> */
  public static final String DELIM_END = "}";

  /** 字符串常量：中括号（左） {@code "["} */
  public static final String BRACKET_START = "[";

  /** 字符串常量：中括号（右） {@code "]"} */
  public static final String BRACKET_END = "]";

  /** 字符串常量：冒号 {@code ":"} */
  public static final String COLON = ":";

  /** 字符串常量：艾特 {@code "@"} */
  public static final String AT = "@";

  /** 字符串常量：空 JSON {@code "{}"} */
  public static final String EMPTY_JSON = "{}";

  /**
   * 调用对象的toString方法，null会返回“null”
   *
   * @param obj 对象
   * @return 字符串
   */
  public static String toString(Object obj) {
    return null == obj ? NULL : obj.toString();
  }

  /**
   * 字符串是否为空白，空白的定义如下：
   *
   * <ol>
   *   <li>{@code null}
   *   <li>空字符串：{@code ""}
   *   <li>空格、全角空格、制表符、换行符，等不可见字符
   * </ol>
   *
   * <p>例：
   *
   * <ul>
   *   <li>{@code StrUtils.isBlank(null) // true}
   *   <li>{@code StrUtils.isBlank("") // true}
   *   <li>{@code StrUtils.isBlank(" \t\n") // true}
   *   <li>{@code StrUtils.isBlank("abc") // false}
   * </ul>
   *
   * <p>注意：该方法与 {@link #isEmpty(CharSequence)} 的区别是： 该方法会校验空白字符，且性能相对于 {@link
   * #isEmpty(CharSequence)} 略慢。 <br>
   *
   * <p>建议：
   *
   * <ul>
   *   <li>该方法建议仅对于客户端（或第三方接口）传入的参数使用该方法。
   *   <li>需要同时校验多个字符串时，建议采用 {@link #hasBlank(CharSequence...)} 或 {@link
   *       #isAllBlank(CharSequence...)}
   * </ul>
   *
   * @param str 被检测的字符串
   * @return 若为空白，则返回 true
   * @see #isEmpty(CharSequence)
   */
  public static boolean isBlank(CharSequence str) {
    int length;

    if ((str == null) || ((length = str.length()) == 0)) {
      return true;
    }

    for (int i = 0; i < length; i++) {
      // 只要有一个非空字符即为非空字符串
      if (!CharUtils.isBlankChar(str.charAt(i))) {
        return false;
      }
    }

    return true;
  }

  /**
   * 字符串是否为非空白，非空白的定义如下：
   *
   * <ol>
   *   <li>不为 {@code null}
   *   <li>不为空字符串：{@code ""}
   *   <li>不为空格、全角空格、制表符、换行符，等不可见字符
   * </ol>
   *
   * <p>例：
   *
   * <ul>
   *   <li>{@code StrUtils.isNotBlank(null) // false}
   *   <li>{@code StrUtils.isNotBlank("") // false}
   *   <li>{@code StrUtils.isNotBlank(" \t\n") // false}
   *   <li>{@code StrUtils.isNotBlank("abc") // true}
   * </ul>
   *
   * <p>注意：该方法与 {@link #isNotEmpty(CharSequence)} 的区别是： 该方法会校验空白字符，且性能相对于 {@link
   * #isNotEmpty(CharSequence)} 略慢。
   *
   * <p>建议：仅对于客户端（或第三方接口）传入的参数使用该方法。
   *
   * @param str 被检测的字符串
   * @return 是否为非空
   * @see #isBlank(CharSequence)
   */
  public static boolean isNotBlank(CharSequence str) {
    return false == isBlank(str);
  }

  /**
   * 指定字符串数组中，是否包含空字符串。
   *
   * <p>如果指定的字符串数组的长度为 0，或者其中的任意一个元素是空字符串，则返回 true。 <br>
   *
   * <p>例：
   *
   * <ul>
   *   <li>{@code StrUtils.hasBlank() // true}
   *   <li>{@code StrUtils.hasBlank("", null, " ") // true}
   *   <li>{@code StrUtils.hasBlank("123", " ") // true}
   *   <li>{@code StrUtils.hasBlank("123", "abc") // false}
   * </ul>
   *
   * <p>注意：该方法与 {@link #isAllBlank(CharSequence...)} 的区别在于：
   *
   * <ul>
   *   <li>hasBlank(CharSequence...) 等价于 {@code isBlank(...) || isBlank(...) || ...}
   *   <li>{@link #isAllBlank(CharSequence...)} 等价于 {@code isBlank(...) && isBlank(...) && ...}
   * </ul>
   *
   * @param strs 字符串列表
   * @return 是否包含空字符串
   */
  public static boolean hasBlank(CharSequence... strs) {
    if (ArrayUtils.isEmpty(strs)) {
      return true;
    }

    for (CharSequence str : strs) {
      if (isBlank(str)) {
        return true;
      }
    }
    return false;
  }

  /**
   * 指定字符串数组中的元素，是否全部为空字符串。
   *
   * <p>如果指定的字符串数组的长度为 0，或者所有元素都是空字符串，则返回 true。 <br>
   *
   * <p>例：
   *
   * <ul>
   *   <li>{@code StrUtils.isAllBlank() // true}
   *   <li>{@code StrUtils.isAllBlank("", null, " ") // true}
   *   <li>{@code StrUtils.isAllBlank("123", " ") // false}
   *   <li>{@code StrUtils.isAllBlank("123", "abc") // false}
   * </ul>
   *
   * <p>注意：该方法与 {@link #hasBlank(CharSequence...)} 的区别在于：
   *
   * <ul>
   *   <li>{@link #hasBlank(CharSequence...)} 等价于 {@code isBlank(...) || isBlank(...) || ...}
   *   <li>isAllBlank(CharSequence...) 等价于 {@code isBlank(...) && isBlank(...) && ...}
   * </ul>
   *
   * @param strs 字符串列表
   * @return 所有字符串是否为空白
   */
  public static boolean isAllBlank(CharSequence... strs) {
    if (ArrayUtils.isEmpty(strs)) {
      return true;
    }

    for (CharSequence str : strs) {
      if (isNotBlank(str)) {
        return false;
      }
    }
    return true;
  }

  /**
   * 字符串是否为空，空的定义如下：
   *
   * <ol>
   *   <li>{@code null}
   *   <li>空字符串：{@code ""}
   * </ol>
   *
   * <p>例：
   *
   * <ul>
   *   <li>{@code StrUtils.isEmpty(null) // true}
   *   <li>{@code StrUtils.isEmpty("") // true}
   *   <li>{@code StrUtils.isEmpty(" \t\n") // false}
   *   <li>{@code StrUtils.isEmpty("abc") // false}
   * </ul>
   *
   * <p>注意：该方法与 {@link #isBlank(CharSequence)} 的区别是：该方法不校验空白字符。
   *
   * <p>建议：
   *
   * <ul>
   *   <li>该方法建议用于工具类或任何可以预期的方法参数的校验中。
   *   <li>需要同时校验多个字符串时，建议采用 {@link #hasEmpty(CharSequence...)} 或 {@link
   *       #isAllEmpty(CharSequence...)}
   * </ul>
   *
   * @param str 被检测的字符串
   * @return 是否为空
   * @see #isBlank(CharSequence)
   */
  public static boolean isEmpty(CharSequence str) {
    return str == null || str.length() == 0;
  }

  /**
   * 字符串是否为非空白，非空白的定义如下：
   *
   * <ol>
   *   <li>不为 {@code null}
   *   <li>不为空字符串：{@code ""}
   * </ol>
   *
   * <p>例：
   *
   * <ul>
   *   <li>{@code StrUtils.isNotEmpty(null) // false}
   *   <li>{@code StrUtils.isNotEmpty("") // false}
   *   <li>{@code StrUtils.isNotEmpty(" \t\n") // true}
   *   <li>{@code StrUtils.isNotEmpty("abc") // true}
   * </ul>
   *
   * <p>注意：该方法与 {@link #isNotBlank(CharSequence)} 的区别是：该方法不校验空白字符。
   *
   * <p>建议：该方法建议用于工具类或任何可以预期的方法参数的校验中。
   *
   * @param str 被检测的字符串
   * @return 是否为非空
   * @see #isEmpty(CharSequence)
   */
  public static boolean isNotEmpty(CharSequence str) {
    return false == isEmpty(str);
  }

  /**
   * 当给定字符串为null时，转换为Empty
   *
   * @param str 被检查的字符串
   * @return 原字符串或者空串
   * @see #nullToEmpty(CharSequence)
   */
  public static String emptyIfNull(CharSequence str) {
    return nullToEmpty(str);
  }

  /**
   * 当给定字符串为null时，转换为Empty
   *
   * @param str 被转换的字符串
   * @return 转换后的字符串
   */
  public static String nullToEmpty(CharSequence str) {
    return nullToDefault(str, EMPTY);
  }

  /**
   * 如果字符串是 {@code null}，则返回指定默认字符串，否则返回字符串本身。
   *
   * <pre>
   * nullToDefault(null, &quot;default&quot;)  = &quot;default&quot;
   * nullToDefault(&quot;&quot;, &quot;default&quot;)    = &quot;&quot;
   * nullToDefault(&quot;  &quot;, &quot;default&quot;)  = &quot;  &quot;
   * nullToDefault(&quot;bat&quot;, &quot;default&quot;) = &quot;bat&quot;
   * </pre>
   *
   * @param str        要转换的字符串
   * @param defaultStr 默认字符串
   * @return 字符串本身或指定的默认字符串
   */
  public static String nullToDefault(CharSequence str, String defaultStr) {
    return (str == null) ? defaultStr : str.toString();
  }

  /**
   * 是否包含空字符串。
   *
   * <p>如果指定的字符串数组的长度为 0，或者其中的任意一个元素是空字符串，则返回 true。 <br>
   *
   * <p>例：
   *
   * <ul>
   *   <li>{@code StrUtils.hasEmpty() // true}
   *   <li>{@code StrUtils.hasEmpty("", null) // true}
   *   <li>{@code StrUtils.hasEmpty("123", "") // true}
   *   <li>{@code StrUtils.hasEmpty("123", "abc") // false}
   *   <li>{@code StrUtils.hasEmpty(" ", "\t", "\n") // false}
   * </ul>
   *
   * <p>注意：该方法与 {@link #isAllEmpty(CharSequence...)} 的区别在于：
   *
   * <ul>
   *   <li>hasEmpty(CharSequence...) 等价于 {@code isEmpty(...) || isEmpty(...) || ...}
   *   <li>{@link #isAllEmpty(CharSequence...)} 等价于 {@code isEmpty(...) && isEmpty(...) && ...}
   * </ul>
   *
   * @param strs 字符串列表
   * @return 是否包含空字符串
   */
  public static boolean hasEmpty(CharSequence... strs) {
    if (ArrayUtils.isEmpty(strs)) {
      return true;
    }

    for (CharSequence str : strs) {
      if (isEmpty(str)) {
        return true;
      }
    }
    return false;
  }

  /**
   * 指定字符串数组中的元素，是否全部为空字符串。
   *
   * <p>如果指定的字符串数组的长度为 0，或者所有元素都是空字符串，则返回 true。 <br>
   *
   * <p>例：
   *
   * <ul>
   *   <li>{@code StrUtils.isAllEmpty() // true}
   *   <li>{@code StrUtils.isAllEmpty("", null) // true}
   *   <li>{@code StrUtils.isAllEmpty("123", "") // false}
   *   <li>{@code StrUtils.isAllEmpty("123", "abc") // false}
   *   <li>{@code StrUtils.isAllEmpty(" ", "\t", "\n") // false}
   * </ul>
   *
   * <p>注意：该方法与 {@link #hasEmpty(CharSequence...)} 的区别在于：
   *
   * <ul>
   *   <li>{@link #hasEmpty(CharSequence...)} 等价于 {@code isEmpty(...) || isEmpty(...) || ...}
   *   <li>isAllEmpty(CharSequence...) 等价于 {@code isEmpty(...) && isEmpty(...) && ...}
   * </ul>
   *
   * @param strs 字符串列表
   * @return 所有字符串是否为空白
   */
  public static boolean isAllEmpty(CharSequence... strs) {
    if (ArrayUtils.isEmpty(strs)) {
      return true;
    }

    for (CharSequence str : strs) {
      if (isNotEmpty(str)) {
        return false;
      }
    }
    return true;
  }

  // ------------------------------------------------------------------------ Trim

  /**
   * 除去字符串头尾部的空白，如果字符串是{@code null}，依然返回{@code null}。
   *
   * <p>注意，和{@link String#trim()}不同，此方法使用{@link CharUtils#isBlankChar(char)} 来判定空白，
   * 因而可以除去英文字符集之外的其它空白，如中文空格。
   *
   * <pre>
   * trim(null)          = null
   * trim(&quot;&quot;)            = &quot;&quot;
   * trim(&quot;     &quot;)       = &quot;&quot;
   * trim(&quot;abc&quot;)         = &quot;abc&quot;
   * trim(&quot;    abc    &quot;) = &quot;abc&quot;
   * </pre>
   *
   * @param str 要处理的字符串
   * @return 除去头尾空白的字符串，如果原字串为{@code null}，则返回{@code null}
   */
  public static String trim(CharSequence str) {
    return (null == str) ? null : trim(str, 0);
  }

  /**
   * 除去字符串头尾部的空白，如果字符串是{@code null}，返回{@code ""}。
   *
   * <pre>
   * StrUtils.trimToEmpty(null)          = ""
   * StrUtils.trimToEmpty("")            = ""
   * StrUtils.trimToEmpty("     ")       = ""
   * StrUtils.trimToEmpty("abc")         = "abc"
   * StrUtils.trimToEmpty("    abc    ") = "abc"
   * </pre>
   *
   * @param str 字符串
   * @return 去除两边空白符后的字符串, 如果为null返回""
   */
  public static String trimToEmpty(CharSequence str) {
    return str == null ? EMPTY : trim(str);
  }

  /**
   * 除去字符串头尾部的空白，如果字符串是{@code null}或者""，返回{@code null}。
   *
   * <pre>
   * StrUtils.trimToNull(null)          = null
   * StrUtils.trimToNull("")            = null
   * StrUtils.trimToNull("     ")       = null
   * StrUtils.trimToNull("abc")         = "abc"
   * StrUtils.trimToEmpty("    abc    ") = "abc"
   * </pre>
   *
   * @param str 字符串
   * @return 去除两边空白符后的字符串, 如果为空返回null
   */
  public static String trimToNull(CharSequence str) {
    final String trimStr = trim(str);
    return EMPTY.equals(trimStr) ? null : trimStr;
  }

  /**
   * 除去字符串头尾部的空白符，如果字符串是{@code null}，依然返回{@code null}。
   *
   * @param str 要处理的字符串
   * @param mode {@code -1}表示trimStart，{@code 0}表示trim全部， {@code 1}表示trimEnd
   * @return 除去指定字符后的的字符串，如果原字串为{@code null}，则返回{@code null}
   */
  public static String trim(CharSequence str, int mode) {
    String result;
    if (str == null) {
      result = null;
    } else {
      int length = str.length();
      int start = 0;
      int end = length; // 扫描字符串头部
      if (mode <= 0) {
        while ((start < end) && (CharUtils.isBlankChar(str.charAt(start)))) {
          start++;
        }
      } // 扫描字符串尾部
      if (mode >= 0) {
        while ((start < end) && (CharUtils.isBlankChar(str.charAt(end - 1)))) {
          end--;
        }
      }
      if ((start > 0) || (end < length)) {
        result = str.toString().substring(start, end);
      } else {
        result = str.toString();
      }
    }

    return result;
  }

  /**
   * 分割字符串
   *
   * @param str 待分割的字符串
   * @param split 分割符字符串
   * @return
   */
  public static List<String> splitTrim(String str, String split) {
    List<String> splitList = new ArrayList<>();
    if (StrUtils.isBlank(str) || StrUtils.isBlank(split)) {
      return splitList;
    }
    String[] strs = str.split(split);
    for (String s : strs) {
      splitList.add(s.trim());
    }
    return splitList;
  }

  /**
   * 分割字符串
   *
   * @param str 待分割的字符串
   * @param split 分割符字符串
   * @return
   */
  public static List<String> split(String str, String split) {
    List<String> splitList = new ArrayList<>();
    if (StrUtils.isBlank(str) || StrUtils.isBlank(split)) {
      return splitList;
    }
    String[] strs = str.split(split);
    splitList.addAll(Arrays.asList(strs));
    return splitList;
  }

  /**
   * 切分字符串，不限制分片数量
   *
   * @param str 被切分的字符串
   * @param separator 分隔符字符
   * @param isTrim 是否去除切分字符串后每个元素两边的空格
   * @param ignoreEmpty 是否忽略空串
   * @return 切分后的集合
   */
  public static List<String> split(
      String str, String separator, boolean isTrim, boolean ignoreEmpty) {
    if (isTrim) {
      return splitTrim(str, separator);
    } else if (ignoreEmpty) {
      List<String> list = split(str, separator);
      list.removeIf(StrUtils::isBlank);
    }
    return split(str, separator);
  }

  /**
   * 分割字符串
   *
   * @param str 待分割的字符串
   * @param split 分割符字符串
   * @return
   */
  public static String[] splitToArray(String str, String split) {
    if (StrUtils.isBlank(str) || StrUtils.isBlank(split)) {
      return new String[] {};
    }
    return str.split(split);
  }

  /**
   * {@link CharSequence} 转为字符串，null安全
   *
   * @param cs {@link CharSequence}
   * @return 字符串
   */
  public static String str(CharSequence cs) {
    return null == cs ? null : cs.toString();
  }

  /**
   * 将byte数组转为字符串
   *
   * @param bytes byte数组
   * @param charset 字符集
   * @return 字符串
   */
  public static String str(byte[] bytes, String charset) {
    return str(bytes, isBlank(charset) ? Charset.defaultCharset() : Charset.forName(charset));
  }

  /**
   * 解码字节码
   *
   * @param data 字符串
   * @param charset 字符集，如果此字段为空，则解码的结果取决于平台
   * @return 解码后的字符串
   */
  public static String str(byte[] data, Charset charset) {
    if (data == null) {
      return null;
    }

    if (null == charset) {
      return new String(data);
    }
    return new String(data, charset);
  }

  /**
   * 将Byte数组转为字符串
   *
   * @param bytes byte数组
   * @param charset 字符集
   * @return 字符串
   */
  public static String str(Byte[] bytes, String charset) {
    return str(bytes, isBlank(charset) ? Charset.defaultCharset() : Charset.forName(charset));
  }

  /**
   * 解码字节码
   *
   * @param data 字符串
   * @param charset 字符集，如果此字段为空，则解码的结果取决于平台
   * @return 解码后的字符串
   */
  public static String str(Byte[] data, Charset charset) {
    if (data == null) {
      return null;
    }

    byte[] bytes = new byte[data.length];
    Byte dataByte;
    for (int i = 0; i < data.length; i++) {
      dataByte = data[i];
      bytes[i] = (null == dataByte) ? -1 : dataByte;
    }

    return str(bytes, charset);
  }

  /**
   * 将编码的byteBuffer数据转换为字符串
   *
   * @param data 数据
   * @param charset 字符集，如果为空使用当前系统字符集
   * @return 字符串
   */
  public static String str(ByteBuffer data, String charset) {
    if (data == null) {
      return null;
    }

    return str(data, Charset.forName(charset));
  }

  /**
   * 将编码的byteBuffer数据转换为字符串
   *
   * @param data 数据
   * @param charset 字符集，如果为空使用当前系统字符集
   * @return 字符串
   */
  public static String str(ByteBuffer data, Charset charset) {
    if (null == charset) {
      charset = Charset.defaultCharset();
    }
    return charset.decode(data).toString();
  }

  /**
   * 将对象转为字符串<br>
   *
   * <pre>
   * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组
   * 2、对象数组会调用Arrays.toString方法
   * </pre>
   *
   * @param obj 对象
   * @return 字符串
   */
  public static String utf8Str(Object obj) {
    return str(obj, CharsetUtils.CHARSET_UTF_8);
  }

  /**
   * 将对象转为字符串
   *
   * <pre>
   * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组
   * 2、对象数组会调用Arrays.toString方法
   * </pre>
   *
   * @param obj 对象
   * @param charsetName 字符集
   * @return 字符串
   */
  public static String str(Object obj, String charsetName) {
    return str(obj, Charset.forName(charsetName));
  }

  /**
   * 将对象转为字符串
   *
   * <pre>
   * 	 1、Byte数组和ByteBuffer会被转换为对应字符串的数组
   * 	 2、对象数组会调用Arrays.toString方法
   * </pre>
   *
   * @param obj 对象
   * @param charset 字符集
   * @return 字符串
   */
  public static String str(Object obj, Charset charset) {
    if (null == obj) {
      return null;
    }

    if (obj instanceof String) {
      return (String) obj;
    } else if (obj instanceof byte[]) {
      return str((byte[]) obj, charset);
    } else if (obj instanceof Byte[]) {
      return str((Byte[]) obj, charset);
    } else if (obj instanceof ByteBuffer) {
      return str((ByteBuffer) obj, charset);
    } else if (ArrayUtils.isArray(obj)) {
      return ArrayUtils.toString(obj);
    }

    return obj.toString();
  }

  /**
   * 格式化字符串<br>
   * 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
   * 如果想输出 {} 使用 \\转义 { 即可，如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
   * 例：<br>
   * 通常使用：format("this is {} for {}", "a", "b") =》 this is a for b<br>
   * 转义{}： format("this is \\{} for {}", "a", "b") =》 this is \{} for a<br>
   * 转义\： format("this is \\\\{} for {}", "a", "b") =》 this is \a for b<br>
   *
   * @param strPattern 字符串模板
   * @param argArray 参数列表
   * @return 结果
   */
  public static String format(final String strPattern, final Object... argArray) {
    if (StrUtils.isBlank(strPattern) || ArrayUtils.isEmpty(argArray)) {
      return strPattern;
    }
    final int strPatternLength = strPattern.length();

    // 初始化定义好的长度以获得更好的性能
    StringBuilder sbuf = new StringBuilder(strPatternLength + 50);

    int handledPosition = 0; // 记录已经处理到的位置
    int delimIndex; // 占位符所在位置
    for (int argIndex = 0; argIndex < argArray.length; argIndex++) {
      delimIndex = strPattern.indexOf(StrUtils.EMPTY_JSON, handledPosition);
      if (delimIndex == -1) { // 剩余部分无占位符
        if (handledPosition == 0) { // 不带占位符的模板直接返回
          return strPattern;
        }
        // 字符串模板剩余部分不再包含占位符，加入剩余部分后返回结果
        sbuf.append(strPattern, handledPosition, strPatternLength);
        return sbuf.toString();
      }

      // 转义符
      if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == StrUtils.C_BACKSLASH) { // 转义符
        if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == StrUtils.C_BACKSLASH) { // 双转义符
          // 转义符之前还有一个转义符，占位符依旧有效
          sbuf.append(strPattern, handledPosition, delimIndex - 1);
          sbuf.append(StrUtils.utf8Str(argArray[argIndex]));
          handledPosition = delimIndex + 2;
        } else {
          // 占位符被转义
          argIndex--;
          sbuf.append(strPattern, handledPosition, delimIndex - 1);
          sbuf.append(StrUtils.C_DELIM_START);
          handledPosition = delimIndex + 1;
        }
      } else { // 正常占位符
        sbuf.append(strPattern, handledPosition, delimIndex);
        sbuf.append(StrUtils.str(argArray[argIndex], StandardCharsets.UTF_8));
        handledPosition = delimIndex + 2;
      }
    }

    // append the characters following the last {} pair.
    // 加入最后一个占位符后所有的字符
    sbuf.append(strPattern, handledPosition, strPattern.length());

    return sbuf.toString();
  }

  /**
   * 包装指定字符串
   *
   * @param str    被包装的字符串
   * @param prefix 前缀
   * @param suffix 后缀
   * @return 包装后的字符串
   */
  public static String wrap(CharSequence str, CharSequence prefix, CharSequence suffix) {
    return nullToEmpty(prefix).concat(nullToEmpty(str)).concat(nullToEmpty(suffix));
  }

  /**
   * 比较字符串内容是否相同
   * @param str1
   * @param str2
   * @param ignoreCase
   * @return
   */
  public static boolean equals(String str1, String str2, boolean ignoreCase) {
    return ignoreCase?equalsIgnoreCase(str1,str2):equals(str1,str2);
  }

  /**
   * 忽略大小写比较字符串内容是否相同
   *
   * @param str1
   * @param str2
   * @return
   */
  public static boolean equalsIgnoreCase(String str1, String str2) {
    if (isBlank(str1) || isBlank(str2)) {
      return false;
    }
    return str1.equalsIgnoreCase(str2);
  }

  /**
   * 比较字符串内容是否相同
   *
   * @param str1
   * @param str2
   * @return
   */
  public static boolean equals(String str1, String str2) {
    if (isBlank(str1) || isBlank(str2)) {
      return false;
    }
    return str1.equals(str2);
  }

  /**
   * @param content
   * @param charset
   * @return
   */
  public static byte[] bytes(String content, Charset charset) {
    return content.getBytes(charset);
  }

  /**
   * 忽略大小写去掉指定前缀
   *
   * @param str 字符串
   * @param prefix 前缀
   * @return 切掉后的字符串，若前缀不是 prefix， 返回原字符串
   */
  public static String removePrefixIgnoreCase(CharSequence str, CharSequence prefix) {
    if (isEmpty(str) || isEmpty(prefix)) {
      return str(str);
    }

    final String str2 = str.toString();
    if (str2.toLowerCase().startsWith(prefix.toString().toLowerCase())) {
      return subSuf(str2, prefix.length()); // 截取后半段
    }
    return str2;
  }

  /**
   * 切割指定位置之后部分的字符串
   *
   * @param string 字符串
   * @param fromIndex 切割开始的位置（包括）
   * @return 切割后后剩余的后半部分字符串
   */
  public static String subSuf(CharSequence string, int fromIndex) {
    if (isEmpty(string)) {
      return null;
    }
    return sub(string, fromIndex, string.length());
  }
  /**
   * 改进JDK subString<br>
   * index从0开始计算，最后一个字符为-1<br>
   * 如果from和to位置一样，返回 "" <br>
   * 如果from或to为负数，则按照length从后向前数位置，如果绝对值大于字符串长度，则from归到0，to归到length<br>
   * 如果经过修正的index中from大于to，则互换from和to example: <br>
   * abcdefgh 2 3 =》 c <br>
   * abcdefgh 2 -3 =》 cde <br>
   *
   * @param str String
   * @param fromIndexInclude 开始的index（包括）
   * @param toIndexExclude 结束的index（不包括）
   * @return 字串
   */
  public static String sub(CharSequence str, int fromIndexInclude, int toIndexExclude) {
    if (isEmpty(str)) {
      return str(str);
    }
    int len = str.length();

    if (fromIndexInclude < 0) {
      fromIndexInclude = len + fromIndexInclude;
      if (fromIndexInclude < 0) {
        fromIndexInclude = 0;
      }
    } else if (fromIndexInclude > len) {
      fromIndexInclude = len;
    }

    if (toIndexExclude < 0) {
      toIndexExclude = len + toIndexExclude;
      if (toIndexExclude < 0) {
        toIndexExclude = len;
      }
    } else if (toIndexExclude > len) {
      toIndexExclude = len;
    }

    if (toIndexExclude < fromIndexInclude) {
      int tmp = fromIndexInclude;
      fromIndexInclude = toIndexExclude;
      toIndexExclude = tmp;
    }

    if (fromIndexInclude == toIndexExclude) {
      return EMPTY;
    }

    return str.toString().substring(fromIndexInclude, toIndexExclude);
  }

  public static boolean startWith(String prefix, char str) {
    return startWith(prefix, String.valueOf(str));
  }

  public static boolean startWith(String prefix, String str) {
    if (isBlank(str)) {
      return false;
    }
    return str.startsWith(prefix);
  }

  public static String join(String demiliter, List<String> elements) {
    if (elements.isEmpty()) {
      return EMPTY;
    }
    return String.join(demiliter, demiliter);
  }

  /**
   * 去除字符串中指定的多个字符，如有多个则全部去除
   *
   * @param str 字符串
   * @param chars 字符列表
   * @return 去除后的字符
   */
  public static String removeAll(CharSequence str, char... chars) {
    if (null == str || ArrayUtils.isEmpty(chars)) {
      return str(str);
    }
    final int len = str.length();
    if (0 == len) {
      return str(str);
    }
    final StringBuilder builder = new StringBuilder(len);
    char c;
    for (int i = 0; i < len; i++) {
      c = str.charAt(i);
      if (false == ArrayUtils.contains(chars, c)) {
        builder.append(c);
      }
    }
    return builder.toString();
  }

  /**
   * 移除字符串中所有给定字符串<br>
   * 例：removeAll("aa-bb-cc-dd", "-") =》 aabbccdd
   *
   * @param str 字符串
   * @param strToRemove 被移除的字符串
   * @return 移除后的字符串
   */
  public static String removeAll(CharSequence str, CharSequence strToRemove) {
    // strToRemove如果为空， 也不用继续后面的逻辑
    if (isEmpty(str) || isEmpty(strToRemove)) {
      return str(str);
    }
    return str.toString().replace(strToRemove, EMPTY);
  }

  public static StringBuilder builder(int length) {
    return new StringBuilder(length);
  }

  public static StringBuilder builder() {
    return new StringBuilder();
  }
}
